package com.liaoyifan.core.filter;

import com.liaoyifan.core.constant.Constants;
import com.liaoyifan.core.constant.Properties;
import com.liaoyifan.core.properties.AuthorizationProperties;
import java.util.Arrays;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.multipart.support.MultipartFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

@SuppressWarnings("unused")
@Configuration
@EnableConfigurationProperties(AuthorizationProperties.class)
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnProperty(name = Properties.WEB_MVC_ENABLE, havingValue = Constants.TRUE)
public class FilterConfiguration {

    @Autowired
    @Qualifier("handlerExceptionResolver") private HandlerExceptionResolver handlerExceptionResolver;

    @Autowired private RequestMappingHandlerMapping requestMappingHandlerMapping;
    @Autowired private AuthorizationProperties authorizationProperties;

    @Bean
    @ConditionalOnMissingBean(CorsFilter.class)
    @ConditionalOnProperty(name = Properties.CORS_FILTER_ENABLE, havingValue = Constants.TRUE)
    public FilterRegistrationBean<CorsFilter> corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOriginPatterns(List.of("*"));
        config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE"));
        config.setAllowCredentials(true);
        config.setAllowedHeaders(List.of("*"));
        config.setExposedHeaders(List.of("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> registration =
                new FilterRegistrationBean<>(new CorsFilter(source));
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 10);
        return registration;
    }

    @Bean
    @ConditionalOnProperty(
            name = Properties.SECURITY_HEADERS_FILTER_ENABLE,
            havingValue = Constants.TRUE)
    public FilterRegistrationBean<SecurityHeadersFilter> securityHeadersFilter() {
        FilterRegistrationBean<SecurityHeadersFilter> registration =
                new FilterRegistrationBean<>(new SecurityHeadersFilter());
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 20);
        return registration;
    }

    @Bean
    @ConditionalOnProperty(
            name = Properties.STATELESS_SESSION_FILTER_ENABLE,
            havingValue = Constants.TRUE)
    public FilterRegistrationBean<StatelessSessionFilter> statelessSessionFilter() {
        FilterRegistrationBean<StatelessSessionFilter> registration =
                new FilterRegistrationBean<>(new StatelessSessionFilter());
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 30);
        return registration;
    }

    @Bean
    public FilterRegistrationBean<OpenApiFilter> OpenApiFilter() {
        FilterRegistrationBean<OpenApiFilter> registration =
                new FilterRegistrationBean<>(new OpenApiFilter(handlerExceptionResolver));
        registration.setOrder(Ordered.HIGHEST_PRECEDENCE + 40);
        registration.setUrlPatterns(List.of("/doc.html", "/v3/api-docs/**", "/webjars/**"));
        return registration;
    }

    @Bean
    @ConditionalOnProperty(
            name = Properties.TRACE_CONTEXT_FILTER_ENABLE,
            havingValue = Constants.TRUE)
    public FilterRegistrationBean<TraceContextFilter> traceContextFilter() {
        FilterRegistrationBean<TraceContextFilter> registration =
                new FilterRegistrationBean<>(new TraceContextFilter());
        registration.setOrder(-100);
        return registration;
    }

    @Bean
    @ConditionalOnMissingBean(MultipartFilter.class)
    @ConditionalOnProperty(name = Properties.MULTIPART_FILTER_ENABLE, havingValue = Constants.TRUE)
    public FilterRegistrationBean<MultipartFilter> multipartFilter() {
        FilterRegistrationBean<MultipartFilter> registration =
                new FilterRegistrationBean<>(new MultipartFilter());
        registration.setOrder(-30);
        return registration;
    }

    @Bean
    @ConditionalOnProperty(
            name = Properties.CACHING_REQUEST_FILTER_ENABLE,
            havingValue = Constants.TRUE)
    public FilterRegistrationBean<CachingRequestFilter> cachingRequestFilter() {
        FilterRegistrationBean<CachingRequestFilter> registration =
                new FilterRegistrationBean<>(new CachingRequestFilter());
        registration.setOrder(-20);
        return registration;
    }

    @Bean
    @ConditionalOnProperty(
            name = Properties.AUTHORIZATION_FILTER_ENABLE,
            havingValue = Constants.TRUE)
    public FilterRegistrationBean<AuthorizationFilter> authorizationFilter() {
        FilterRegistrationBean<AuthorizationFilter> registration = new FilterRegistrationBean<>();
        registration.setFilter(
                new AuthorizationFilter(
                        handlerExceptionResolver,
                        requestMappingHandlerMapping,
                        authorizationProperties));
        registration.setOrder(-10);
        return registration;
    }
}
